﻿using System;
using System.Configuration;
using System.Threading;
using YEA.Infrastructure.Gateway;
using YEA.Monitor.LogManager;
using YEA.Utilities.Service.Controller;
using System.Diagnostics;

namespace YEA.Monitor.LogService
{
    public class Controller : IServiceController
    {
        public const string LogReceiverKey = "LogReceiverName";
        public string logReceiverName;

        protected enum Diagnostic { MessageBusUnavailable, FatalForUnknownReasons, Unknown };
        protected IAsyncConsumerProcessor receiver;
        protected MessageHandler mh;
        protected log4net.ILog logger;
        protected Stopwatch processTimer;
        protected ServiceStatus status;
        protected int ExceptionCounter;
        public IGatewayFactory GatewayFactory { get; set; }

        public event EventHandler<EventArgs<ServiceStatus>> StatusChanged;

        public Controller()
        {
            logReceiverName = ConfigurationManager.AppSettings[LogReceiverKey];
            if( string.IsNullOrEmpty(logReceiverName))
                throw new ConfigurationErrorsException(string.Format("the configuration key {0} is required, please add it to the app setting section of your configuration file", LogReceiverKey));
            processTimer = new Stopwatch();
            status = ServiceStatus.Initiated;
            ExceptionCounter = 0;
            mh = new MessageHandler();
        }
            
        
        public Controller( IGatewayFactory gf)
            : this()
        {
            GatewayFactory = gf;
        }
        private void SendStatusEvent(ServiceStatus status)
        {
            this.status = status;
            var e = new EventArgs<ServiceStatus>(status);
            if (StatusChanged != null)
                StatusChanged(this, e);
        }
        public void Start()
        {
            if (receiver == null)
            {
                logger = Logger.Get("YEA.Monitor.LogService");
                processTimer.Restart();
                try
                {
                    if (GatewayFactory == null)
                        GatewayFactory = new GatewayFactory();
                    SendStatusEvent(ServiceStatus.InProgress);
                    receiver = GatewayFactory.GetAsyncReceiver(logReceiverName, mh.ConsumeMessage);
                }
                catch (Exception ex)
                {
                    Interpret(ex);
                }
            }
        }


        public void Stop()
        {
            SendStatusEvent(ServiceStatus.Stopped);
            if( receiver != null)
                receiver.Stop();
            mh.Dispose();
            GatewayFactory.Dispose();
        }

        public void Restart()
        {
            Stop();
            Start();
        }

        public void Pause()
        {
            SendStatusEvent(ServiceStatus.Paused);
        }

        public void Continue()
        {
            SendStatusEvent(ServiceStatus.InProgress);
        }

        public void Interpret(Exception e)
        {

            logger.Error(e.Message, e);
            if (status == ServiceStatus.Stopped)
                return;
            SendStatusEvent(ServiceStatus.Disabled);

            switch (Diagnose(e))
            {
                case Diagnostic.MessageBusUnavailable :
                    Recover(e);
                    break;
                case Diagnostic.Unknown: 
                    logger.Error(e.Message, e);
                    Restart();
                    break;
                case Diagnostic.FatalForUnknownReasons: 
                    Stop();
                    logger.Fatal(e.Message, e);
                    break;
            }

        }
        public void Recover(Exception e)
        {
            SendStatusEvent(ServiceStatus.Recovering);
            if (status != ServiceStatus.Stopped)
            {
                Thread.Sleep(1000);
                this.Start();
            }
            
        }
        protected Diagnostic Diagnose(Exception e)
        {
            resetCounter();
            if (e is RabbitMQ.Client.Exceptions.OperationInterruptedException
                || e is RabbitMQ.Client.Exceptions.BrokerUnreachableException
                || e is RabbitMQ.Client.Exceptions.AlreadyClosedException)
            {
                return Diagnostic.MessageBusUnavailable;

            }
            if (ExceptionCounter++ < 100)
                return Diagnostic.Unknown;
            return Diagnostic.FatalForUnknownReasons;
        }
        private void resetCounter()
        {
            if (processTimer.Elapsed > TimeSpan.FromMinutes(10.0))
            {
                ExceptionCounter = 0;
            }
        }
    }
}
